import { Injectable, Logger, OnModuleInit, OnModuleDestroy } from '@nestjs/common';
import { PrismaClient } from '@prisma/client';
import { ConfigService } from '@nestjs/config';

/**
 * Prisma数据库服务
 * 零隐患MFIS规范 - 解决隐患：#9 数据库事务，#10 重试策略
 */
@Injectable()
export class PrismaService extends PrismaClient implements OnModuleInit, OnModuleDestroy {
  private readonly logger = new Logger(PrismaService.name);

  constructor(private readonly configService: ConfigService) {
    super({
      log: [
        {
          emit: 'event',
          level: 'query',
        },
        {
          emit: 'event',
          level: 'error',
        },
        {
          emit: 'event',
          level: 'info',
        },
        {
          emit: 'event',
          level: 'warn',
        },
      ],
      errorFormat: 'pretty',
    });
  }

  async onModuleInit() {
    // 配置日志事件
    this.$on('query', (e) => {
      // 只在开发环境记录查询日志
      if (this.configService.get<string>('NODE_ENV') === 'development') {
        this.logger.debug('数据库查询', {
          query: e.query,
          params: e.params,
          duration: `${e.duration}ms`,
          timestamp: e.timestamp,
        });
      }
    });

    this.$on('error', (e) => {
      this.logger.error('数据库错误', {
        target: e.target,
        message: e.message,
      });
    });

    this.$on('info', (e) => {
      this.logger.log('数据库信息', {
        target: e.target,
        message: e.message,
      });
    });

    this.$on('warn', (e) => {
      this.logger.warn('数据库警告', {
        target: e.target,
        message: e.message,
      });
    });

    await this.$connect();
  }

  async onModuleDestroy() {
    await this.$disconnect();
  }

  /**
   * 执行带重试的数据库操作
   * 解决隐患：#10 数据库重试策略
   */
  async executeWithRetry<T>(
    operation: () => Promise<T>,
    maxRetries: number = 3,
    baseDelay: number = 1000,
  ): Promise<T> {
    let lastError: Error;

    for (let attempt = 1; attempt <= maxRetries; attempt++) {
      try {
        return await operation();
      } catch (error) {
        lastError = error;

        // 检查是否为可重试的错误
        if (!this.isRetryableError(error)) {
          throw error;
        }

        // 最后一次尝试失败
        if (attempt === maxRetries) {
          this.logger.error('数据库重试失败', {
            attempts: maxRetries,
            error: error.message,
          });
          throw error;
        }

        // 计算延迟时间（指数退避 + 抖动）
        const delay = this.calculateDelay(baseDelay, attempt);
        this.logger.warn(`数据库操作失败，${delay}ms后重试 (${attempt}/${maxRetries})`, {
          error: error.message,
          attempt,
        });

        await this.sleep(delay);
      }
    }

    throw lastError;
  }

  /**
   * 检查错误是否可重试
   */
  private isRetryableError(error: any): boolean {
    const retryablePatterns = [
      /connection/i,
      /timeout/i,
      /deadlock/i,
      /lock wait timeout/i,
      /connection reset/i,
      /ECONNRESET/i,
      /ETIMEDOUT/i,
    ];

    return retryablePatterns.some(pattern =>
      pattern.test(error.message || '')
    );
  }

  /**
   * 计算重试延迟（指数退避 + 抖动）
   */
  private calculateDelay(baseDelay: number, attempt: number): number {
    const exponentialDelay = baseDelay * Math.pow(2, attempt - 1);
    const jitter = Math.random() * 0.3 * exponentialDelay; // 30% 抖动
    return Math.min(exponentialDelay + jitter, 30000); // 最大30秒
  }

  /**
   * 休眠函数
   */
  private sleep(ms: number): Promise<void> {
    return new Promise(resolve => setTimeout(resolve, ms));
  }

  /**
   * 健康检查
   */
  async healthCheck(): Promise<any> {
    try {
      const startTime = Date.now();
      await this.$queryRaw`SELECT 1`;
      const responseTime = Date.now() - startTime;

      return {
        status: 'healthy',
        response_time_ms: responseTime,
        timestamp: new Date().toISOString(),
      };
    } catch (error) {
      return {
        status: 'unhealthy',
        error: error.message,
        timestamp: new Date().toISOString(),
      };
    }
  }
}